package org.rcgwt.client.ui.calendar.renderer.impl;

import org.rcgwt.client.ui.calendar.controller.CalendarAction;
import org.rcgwt.client.ui.calendar.controller.CalendarChangesMask;
import org.rcgwt.client.ui.calendar.controller.CalendarController;
import org.rcgwt.client.ui.calendar.event.ModelChangedEvent;
import org.rcgwt.client.ui.calendar.event.ModelChangedHandler;
import org.rcgwt.client.ui.calendar.i18n.CalendarUtils;
import org.rcgwt.client.ui.calendar.model.impl.CalendarModel;
import org.rcgwt.client.ui.calendar.model.impl.SelectionType;
import org.rcgwt.client.ui.calendar.mvc.ICalendarContext;
import org.rcgwt.client.ui.calendar.renderer.IHeaderRenderer;
import org.rcgwt.client.ui.themes.ITheme;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.Cell;

/**
 * A Month & Year cursor on a single line
 * 
 * @author Elian ORIOU
 * 
 */

public class SingleLineCalendarHeaderRenderer implements IHeaderRenderer {

	/* Constants */

	private static final String[] months = CalendarUtils.getMonths();

	private CalendarController controller;
	private CalendarModel model;
	private ITheme theme;

	private FlexTable rootPanel;

	private Label b_previous;
	private Label b_next;

	private Label mainLabel;

	public SingleLineCalendarHeaderRenderer() {

	}

	@Override
	public Widget render(ICalendarContext context) {

		controller = context.getController();
		model = context.getModel();
		theme = context.getTheme();

		rootPanel = new FlexTable();
		rootPanel
				.addStyleName(theme.getYearAndMonthPanelStyle().getClassName());

		b_previous = new Label();
		b_previous.addStyleName(theme.getPreviousButtonStyle().getClassName());
		b_next = new Label();
		b_next.addStyleName(theme.getNextButtonStyle().getClassName());

		mainLabel = new Label();
		mainLabel.addStyleName(theme.getMonthLabelStyle().getClassName());

		rootPanel.setWidget(0, 0, b_previous);

		Node body = rootPanel.getElement().getLastChild();
		Node line = body.getChild(0);

		Element lab = DOM.createTD();
		lab.addClassName(theme.getSingleLineColumnStyle().getClassName());
		lab.appendChild(mainLabel.getElement());
		line.appendChild(lab);

		rootPanel.setWidget(0, 2, b_next);
		createHandlers();

		return rootPanel;
	}

	private void createHandlers() {

		b_previous.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
			}
		});

		b_next.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				controller.handle(CalendarAction.NEXT_MONTH);
			}
		});

		controller.addModelChangedHandler(new ModelChangedHandler() {
			@Override
			public void onModelChangedEvent(ModelChangedEvent event) {
				int type = event.getElementType();
				int dy = model.getDisplayedYear();
				int dm = model.getDisplayedMonth();

				b_previous.setStyleName(theme.getPreviousButtonStyle()
						.getClassName());
				b_next.setStyleName(theme.getNextButtonStyle().getClassName());

				if ((type & (CalendarChangesMask.SPECIFIED_MONTH | CalendarChangesMask.SPECIFIED_YEAR)) > 0) {
					displayDate(dm, dy);
				}
				if ((type & (CalendarChangesMask.SELECT_MONTH | CalendarChangesMask.SELECT_YEAR)) > 0) {
					selectLabel(mainLabel);
				}
				if ((type & (CalendarChangesMask.UNSELECT_MONTH | CalendarChangesMask.UNSELECT_YEAR)) > 0) {
					deselectLabel(mainLabel);
				}
				if ((type & CalendarChangesMask.MIN_OVERFLOW) > 0) {
					b_previous.setStyleName(theme
							.getDisabledPreviousButtonStyle().getClassName());
				}
				if ((type & CalendarChangesMask.MAX_OVERFLOW) > 0) {
					b_next.setStyleName(theme.getDisabledNextButtonStyle()
							.getClassName());
				}
			}
		});

		rootPanel.sinkEvents(Event.ONCLICK);
		rootPanel.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				Cell c = rootPanel.getCellForEvent(event);
				if (c == null) {
					return;
				}
				int cell = c.getCellIndex();
				if (cell == 1) {
					selectLabel(mainLabel);
					controller.setSelectionType(SelectionType.MONTH);
					controller.handle(CalendarAction.SELECTION_TYPE);
				}
			}
		});
	}

	private void displayDate(int month, int year) {
		String m = months[month];
		mainLabel.setText(m + " " + year);
	}

	private void selectLabel(Label l) {
		l.addStyleName(theme.getSelectedYearMonthLabelStyle().getClassName());
	}

	private void deselectLabel(Label l) {
		l
				.removeStyleName(theme.getSelectedYearMonthLabelStyle()
						.getClassName());
	}

	@Override
	public void onKeyDown(KeyDownEvent event) {
		SelectionType type = model.getSelectionType();
		if (type != SelectionType.MONTH) {
			return;
		}
		int keyCode = event.getNativeKeyCode();
		switch (keyCode) {
		case KeyCodes.KEY_LEFT:
			controller.handle(CalendarAction.PREVIOUS_MONTH);
			break;
		case KeyCodes.KEY_RIGHT:
			controller.handle(CalendarAction.NEXT_MONTH);
			break;
		case KeyCodes.KEY_HOME:
			if (model.getDisplayedMonth() == 0) {
				controller.handle(CalendarAction.PREVIOUS_YEAR);
			}
			controller.setDisplayedMonth(0);
			controller.handle(CalendarAction.REFRESH_CALENDAR);
			break;
		case KeyCodes.KEY_END:
			if (model.getDisplayedMonth() == 11) {
				controller.handle(CalendarAction.NEXT_YEAR);
			}
			controller.setDisplayedMonth(11);
			controller.handle(CalendarAction.REFRESH_CALENDAR);
			break;
		}
	}

	@Override
	public void update(ICalendarContext context) {
	}

	@Override
	public void onKeyUp(KeyUpEvent event) {
	}

	@Override
	public void onMouseWheel(MouseWheelEvent event) {
		SelectionType type = model.getSelectionType();
		if (type != SelectionType.YEAR && type != SelectionType.MONTH) {
			return;
		}
		event.preventDefault();
		if (event.isNorth() == true) {
			controller.handle(CalendarAction.PREVIOUS_MONTH);
		} else if (event.isSouth() == true) {
			controller.handle(CalendarAction.NEXT_MONTH);
		}
	}
}
